﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using DailyDiet.Models;
using Microcharts;
using SkiaSharp;
using Xamarin.Forms;

namespace DailyDiet.Services.Impl
{
/*
 *注意，在查询历史记录的时候
 * 首先查出来的List<Recorder>
 * 这里将List<Recorder>转换为LIst<Entry>分为两步
 * 首先将其转换为List<Diet>,再转换为List<Entry>
 * 至于为什么要写两个函数，分成两步，我也不知道是怎么想的
 * 可能我觉得中间物List<Diet>会有用吧哈哈哈哈哈啊哈哈
 */
    public class RecorderUtilService : IRecorderUtilService {
        private IDietStorageService _dietStorageService;
        private IDietRecorderStorageService _dietRecorderStorageService;

        private string[] _properties = DietConstant.Properties;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dietStorageService"></param>
        public RecorderUtilService(
            IDietStorageService dietStorageService,
            IDietRecorderStorageService dietRecorderStorageService) {
            _dietStorageService = dietStorageService;
            _dietRecorderStorageService = dietRecorderStorageService;
        }

        /// <summary>
        /// 查询某一天特定餐的历史记录，警惕null值
        /// </summary>
        /// <param name="MealTimes"></param>
        /// <param name="date">使用DietRecorderConstant中的mealTime常量</param>
        /// <returns></returns>
        public async Task<List<DietRecorder>> GetDietRecordersByMealTimesAsync(string mealTimes, DateTime date) {

            // 按照recorder数据库要求，这里应该为 yyyy-MM-dd 格式的日期字符串
            string dateString = "";
            if (!DietRecorderConstant.MealTimes.Contains(mealTimes) ||
                date == null) {
                return null;
            }
            try {
                dateString = date.ToString("yyyy-MM-dd");
            } catch (Exception e) {
                Console.WriteLine(e);
            }
            Expression<Func<DietRecorder, bool>> where = p =>
                p.Date.Equals(dateString) && p.MealTimes.Equals(mealTimes);
            //这里不应该有skip和take，但是数据库设计缺陷没办法
            return await _dietRecorderStorageService.GetDietRecordersAsync(where,0,10);
        }

        /// <summary>
        /// 查询某一天的记录 警惕null值
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public async Task<List<DietRecorder>> GetDietRecordersByDateAsync(DateTime date) {
            List<DietRecorder> dietRecorders = new List<DietRecorder>();

            // 按照recorder数据库要求，这里应该为 yyyy-MM-dd 格式的日期字符串
            string dateString = "";
            if (date == null)
            {
                return null;
            }
            try
            {
                dateString = date.ToString("yyyy-MM-dd");
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            Expression<Func<DietRecorder, bool>> where = p =>
                p.Date.Equals(dateString);
            //这里不应该有skip和take，但是数据库设计缺陷没办法
            return await _dietRecorderStorageService.GetDietRecordersAsync(where, 0, 10);
        }

        /// <summary>
        /// 将List<Diet>转换为List<Entry>
        /// </summary>
        /// <param name="diets"></param>
        /// <returns></returns>
        public List<ChartEntry> ConvertListIntoChartEntries(List<Diet> diets) {
            Dictionary<string, double> sumDict =
                new Dictionary<string, double>();

            foreach (var property in _properties) {
                sumDict[property] = diets.Sum(x =>
                    (x == null
                        ? 0
                        : (double) x.GetType().GetProperty(property)
                            .GetValue(x, null)));
            }

            List<ChartEntry> entries = new List<ChartEntry>();
            DietStander dietStander = new DietStander();
            foreach (var property in _properties)
            {
                if (!sumDict.Keys.Contains(property))
                {
                    continue;
                }
                double stander = (double)dietStander.GetType().GetProperty(property)
                    .GetValue(dietStander, null);
                double p = sumDict[property] / (stander * 3) * 100;
                entries.Add(new ChartEntry((float) (p <= 100 ? p : 100))
                {
                    Label = property,
                    ValueLabel = p.ToString("f2") + "%",
                    Color = SKColor.Parse(DietConstant.PropertiesToColorsDict[property])
                });
            }

            return entries;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dictionary"></param>
        /// <param name="properties"></param>
        /// <returns></returns>
        public List<ChartEntry> ConvertDictIntoChartEntries(Dictionary<string, List<Diet>> dictionary, string property) {
            if (!_properties.Contains(property)) {
                return null;
            }

            List<ChartEntry> entries = new List<ChartEntry>();

            foreach (var key in dictionary.Keys) {
                List<Diet> diets = dictionary[key];
                double value = diets.Sum(x =>
                   ((x==null)?0: (double)x.GetType().GetProperty(property)
                        .GetValue(x, null)));
                entries.Add(new ChartEntry((float)value) {
                    Label = key.Substring(5),
                    ValueLabel = Convert.ToString(value)
                });
            }

            return entries;
        }

        /// <summary>
        /// 将List<DietRecorder>转换为List<Diet>
        /// </summary>
        /// <param name="dietRecorders"></param>
        /// <returns></returns>
        public async Task<List<Diet>> ConvertRecordersIntoDietsAsync(List<DietRecorder> dietRecorders) {
            List<Diet> diets = new List<Diet>();
            foreach (var dietRecorder in dietRecorders)
            {
                diets.Add(await _dietStorageService.GetDietAsync(dietRecorder.DietId));
            }
            return diets;
        }
        /*
         *比如说长期的
         */

        /// <summary>
        /// 获取一个key为dateTime，vale为List<Recorder>的字典
        /// </summary>
        /// <param name="date"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, List<Diet>>> GetLongTermDietRecorderAsync(DateTime date, string model) {
            if (date == null ||
                !RecorderUtilConstant.LongTermModels.Contains(model)) {
                return null;
            }

            Dictionary<string, List<Diet>> recorderDict =
                new Dictionary<string, List<Diet>>();

            List<DateTime> dateTimes = GetAroundDateTime(date, model);
            foreach (var dateTime in dateTimes) {
                var dateStr = dateTime.ToString("yyyy-MM-dd");
                Expression<Func<DietRecorder, bool>> where = p =>
                    p.Date.Equals(dateStr);
                recorderDict[dateStr] = await ConvertRecordersIntoDietsAsync(
                    await _dietRecorderStorageService.GetDietRecordersAsync(
                        where, 0, 20)); 
            }

            return recorderDict;
        }

        /*
         *这个用来现实长期记录的
         * 比如用户选择 2021/12/9号 星期四
         * 那么这个周的记录就应该是 6 7 8 9 10 11 12 13这七天
         * 因此设计这个函数就是希望传入 2021/12/9，返回一个list<Datetime>,包含6 7 8 9 10 11 12 13这七天
         */
        /// <summary>
        /// 获取周围时间
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public List<DateTime> GetAroundDateTime(DateTime date, string mode) {
            int MAX = 32;

            if (date == null) {
                return null;
            }

            if (!RecorderUtilConstant.LongTermModels.Contains(mode)) {
                mode = RecorderUtilConstant.LongTermModelWeek;
            }

            DateTime start;
            DateTime end;
            DateTime now = new DateTime(DateTime.Now.Year, DateTime.Now.Month,
                DateTime.Now.Day);
            if (RecorderUtilConstant.LongTermModelWeek.Equals(mode)) {
                int dayOfWeek = Convert.ToInt32(date.DayOfWeek.ToString("d"));
                start = date.AddDays(1 - (dayOfWeek == 0 ? 7 : dayOfWeek));

                end = start.AddDays(6) < now
                    ? start.AddDays(6)
                    : now;
            } else {
                start = date.AddDays(1 - date.Day);  //本月月初
                end = start.AddMonths(1).AddDays(-1) < now
                    ? start.AddMonths(1).AddDays(-1)
                    : now; //本月月末
            }
            
            int year = start.Year;
            int month = start.Month;
            int day = start.Day;

            List<DateTime> dateTimes = new List<DateTime>();

            for(int i = 0; i<MAX;i++) {
                DateTime dateTemp = start + TimeSpan.FromDays(i);
                if (dateTemp >= end) {
                    dateTimes.Add(dateTemp);
                    break;
                }
                dateTimes.Add(dateTemp);
            }

            return dateTimes;
        }
    }
}
